#ifndef _OCPP_WorkQueue_H__
#define _OCPP_WorkQueue_H__

#include "RequestHandler.h"
#include "ResponseHandler.h"

namespace OCPP
{
	class _MainExport WorkQueue
	{
	protected:
		typedef std::map<String, uint16> ChannelMap;
		ChannelMap mChannelMap;
		uint16 mNextChannel;
		CORE_MUTEX(mChannelMapMutex);
	public:
		/// Numeric identifier for a request
		WorkQueue() : mNextChannel(0) {}
		virtual ~WorkQueue() {}

		/** Start up the queue with the options that have been set.
		@param forceRestart If the queue is already running, whether to shut it
			down and restart.
		*/
		virtual void startup(bool forceRestart = true) = 0;
		/** Add a request handler instance to the queue.
		@remarks
			Every queue must have at least one request handler instance for each
			channel in which requests are raised. If you
			add more than one handler per channel, then you must implement canHandleRequest
			differently	in each if you wish them to respond to different requests.
		@param channel The channel for requests you want to handle
		@param rh Your handler
		*/
		virtual void addRequestHandler(uint16 channel, RequestHandler* rh) = 0;
		/** Remove a request handler. */
		virtual void removeRequestHandler(uint16 channel, RequestHandler* rh) = 0;

		/** Add a response handler instance to the queue.
		@remarks
			Every queue must have at least one response handler instance for each
			channel in which requests are raised. If you add more than one, then you
			must implement canHandleResponse differently in each if you wish them
			to respond to different responses.
		@param channel The channel for responses you want to handle
		@param rh Your handler
		*/
		virtual void addResponseHandler(uint16 channel, ResponseHandler* rh) = 0;
		/** Remove a Response handler. */
		virtual void removeResponseHandler(uint16 channel, ResponseHandler* rh) = 0;

		/** Add a new request to the queue.
		@param channel The channel this request will go into = 0; the channel is the top-level
			categorisation of the request
		@param requestType An identifier that's unique within this queue which
			identifies the type of the request (user decides the actual value)
		@param rData The data required by the request process.
		@param retryCount The number of times the request should be retried
			if it fails.
		@param forceSynchronous Forces the request to be processed immediately
			even if threading is enabled.
		@return The ID of the request that has been added
		*/
		virtual RequestID addRequest(uint16 channel, uint16 requestType, const Any& rData, int priority = 0
			, uint8 retryCount = 0,
			bool forceSynchronous = false) = 0;

		/** Abort a previously issued request.
		If the request is still waiting to be processed, it will be
		removed from the queue.
		@param id The ID of the previously issued request.
		*/
		virtual void abortRequest(RequestID id) = 0;

		/** Abort all previously issued requests in a given channel.
		Any requests still waiting to be processed of the given channel, will be
		removed from the queue.
		@param channel The type of request to be aborted
		*/
		virtual void abortRequestsByChannel(uint16 channel) = 0;

		/** Abort all previously issued requests.
		Any requests still waiting to be processed will be removed from the queue.
		Any requests that are being processed will still complete.
		*/
		virtual void abortAllRequests() = 0;

		/** Set whether to pause further processing of any requests.
		If true, any further requests will simply be queued and not processed until
		setPaused(false) is called. Any requests which are in the process of being
		worked on already will still continue.
		*/
		virtual void setPaused(bool pause) = 0;
		/// Return whether the queue is paused ie not sending more work to workers
		virtual bool isPaused() const = 0;

		/** Set whether to accept new requests or not.
		If true, requests are added to the queue as usual. If false, requests
		are silently ignored until setRequestsAccepted(true) is called.
		*/
		virtual void setRequestsAccepted(bool accept) = 0;
		/// Returns whether requests are being accepted right now
		virtual bool getRequestsAccepted() const = 0;

		/** Process the responses in the queue.
		@remarks
			This method is public, and must be called from the main render
			thread to 'pump' responses through the system. The method will usually
			try to clear all responses before returning = 0; however, you can specify
			a time limit on the response processing to limit the impact of
			spikes in demand by calling setResponseProcessingTimeLimit.
		*/
		virtual void processResponses() = 0;

		/** Get the time limit imposed on the processing of responses in a
			single frame, in milliseconds (0 indicates no limit).
		*/
		virtual unsigned long getResponseProcessingTimeLimit() const = 0;

		/** Set the time limit imposed on the processing of responses in a
			single frame, in milliseconds (0 indicates no limit).
			This sets the maximum time that will be spent in processResponses() in
			a single frame. The default is 8ms.
		*/
		virtual void setResponseProcessingTimeLimit(unsigned long ms) = 0;

		/** Shut down the queue.
		*/
		virtual void shutdown() = 0;

		/** Get a channel ID for a given channel name.
		@remarks
			Channels are assigned on a first-come, first-served basis and are
			not persistent across application instances. This method allows
			applications to not worry about channel clashes through manually
			assigned channel numbers.
		*/
		virtual uint16 getChannel(const String& channelName);
	};
}

#endif
